#ifndef ODBC_DATASET_READER_HPP
#define ODBC_DATASET_READER_HPP

#include "odbc/column.hpp"
#include "odbc/details/statement_visitor.hpp"
#include <boost/tuple/tuple.hpp>
#include <boost/any.hpp>
#include <vector>

namespace odbc
{

namespace details
{
	template< class Statement, class T>
	static void bind_buffer(
		SQLHANDLE h_stat, std::vector< column< typename Statement::execution_policy>* >& columns, unsigned short index)
	{
		typedef column< typename Statement::execution_policy> column_type;
		// TODO: Here, we rely on a predefined buffer size. It has to be changed.
		std::auto_ptr< column_type> p_new_col( new column_type( h_stat, details::buffer<T>(), index));
		columns.push_back( p_new_col.release());
	}

	// TODO: Review comments. They need update

	/// \internal
	/// \brief
	///		It binds the column corresponding to the index given as template parameter.
	/// \param Index
	///		The index of the column to bind the buffer for. The index is also used at compile time to get the type of
	///		the data for the column to be bound, from the tuple type parameter of the parent class (data_reader).
	/// \details
	///		This class provides a compile time code generation for binding column buffers corresponding to the types in
	///		the tuple the data_reader class is parameterized with.
	///		\n
	///		The class is derived from the same class, parameterized with a decremented index, and the buffer binding is
	///		performed in the constructor. This way, the first binding is for column 1.
	template< class Statement, class Tuple, unsigned short Index = boost::tuples::length<Tuple>::value - 1>
	class column_binder: column_binder<Statement, Tuple, Index - 1>
	{
		typedef column< typename Statement::execution_policy> column_type;
		typedef std::vector< column_type*> column_collection;
		typedef column_binder< Statement, Tuple, Index - 1> base_class;
		typedef typename boost::tuples::element< Index, Tuple>::type data_type;
	public:
		/// \brief It binds the column to the statement. The position of the column is given by the index template
		///		parameter.
		/// \param statToRead
		///		the statement to bind the buffer to.
		/// \param buffers
		///		the collection to add the newly created and bound column buffer. The buffer collection is actually the
		///		data member from data_reader outer class.
		column_binder( SQLHANDLE stat_h, column_collection& columns)
			: base_class( stat_h, columns)
		{
			bind_buffer< Statement, data_type>( stat_h, columns, Index + 1);
		}
	};

	/// \internal
	/// \brief End of compile time recursion for column binding.
	template< class Statement, class Tuple>
	class column_binder< Statement, Tuple, 0>
	{
		typedef column< typename Statement::execution_policy> column_type;
		typedef std::vector< column_type*> column_collection;
		typedef typename boost::tuples::element<0, Tuple>::type data_type;
	public:
		column_binder( SQLHANDLE stat_h, column_collection& columns)
		{
			bind_buffer< Statement, data_type>( stat_h, columns, 1);
		}
	};

} // namespace details

// TODO: Document
template< class Tuple, class Statement>
class dataset_reader: public details::statement_visitor< Statement>
{
	typedef typename Statement::execution_policy execution_policy;
	typedef Statement statement;
	typedef column< execution_policy> column_type;
	typedef std::vector< column_type*> column_collection;
	typedef Tuple tuple_type;
	typedef std::vector< boost::any> record_type;
public:
	dataset_reader( Statement& stat)
		: statement_( stat)
		, current_record_( boost::tuples::length< tuple_type>::value)
	{
		SQLRETURN retCode = SQLNumResultCols( get_statement_handle( stat), &no_of_cols_);
		throw_if_stat_failed( retCode, get_statement_handle( stat));

		details::column_binder<Statement, Tuple> binder( get_statement_handle( stat), columns_);
	}

	~dataset_reader()
	{
		BOOST_FOREACH( column_type* p_col, columns_)
		{
			delete p_col;
		}
	}

	/// \brief It gets the number of the columns resulted from statement execution.
	unsigned int numberOfColumns() const { return static_cast< unsigned int>( no_of_cols_); }

	/// \ingroup odbcpp
	/// \brief
	///		It provides the intuitive way of iterating through the data set, without modifying the iterated record
	///		object.
	/// \details
	///		The iterated element (\c value_type) is the \c \link record \endlink element.
	///		\n
	///		A valid iterator is the one having a not null dataset statement reference. An \c end iterator is the one
	///		having a null dataset statement reference.
	class const_iterator
		: public std::iterator< std::forward_iterator_tag, record_type, std::size_t, std::size_t, record_type&>
	{
	public:
		/// \brief
		///		It initializes a valid iterator, using the specified dataset
		///		statement.
		/// \param stat the dataset statement providing the data.
		/// \details
		///		It tries to fetch the first record, so it contains valid data in case of valid iterator. However, if no
		///		data is available for fetching, the associated dataset statement will be set to NULL and the iterator
		///		will look like an \c end iterator.
		const_iterator( dataset_reader& stat)
			: p_reader_(  &stat)
			, ended_( !stat.fetch())
		{

		}


		///	\brief
		///		It initializes an \c end iterator.
		const_iterator()
			: p_reader_( NULL)
			, ended_( true)
		{
		}

		/// \brief
		///		It fetches the next record from the source dataset.
		/// \details
		///		If there is no record to be fetched, the iterator becomes an \c end iterator.
		const_iterator& operator++( int)
		{
			p_reader_ = (ended_ = !p_reader_->fetch()) ? NULL : p_reader_ ;
			return *this;
		}

		/// \brief
		///		It fetches the next record from the source dataset.
		/// \details
		///		If there is no record to be fetched, the iterator becomes an \c end iterator.
		const_iterator operator++()
		{
			const_iterator cpy( *this);
			p_reader_ = (ended_ = !p_reader_->fetch()) ? NULL : p_reader_;
			return cpy;
		}

		///	\brief
		///		It compares two iterators.
		/// \return
		///		true if the two iterators are equal or false otherwise.
		/// \note
		///		Due to the limitations on iterations (see \c \link dataset_statement \endlink class documentation), two
		///		iterators are equal if they refer to the same dataset.
		///		\n
		///		Two \c end iterators are always equal, since they refer to a null dataset statement reference.
		bool operator==( const const_iterator& op2) const
		{
			return p_reader_ == op2.p_reader_ && ended_ == op2.ended_;
		}

		///	\brief
		///		It compares two iterators.
		/// \return
		///		true if the two iterators are equal or false otherwise.
		/// \note
		///		Due to the limitations on iterations (see \c \link dataset_statement \endlink class documentation), two
		///		iterators are equal if they refer to the same dataset.
		///		\n
		///		Two \c end iterators are always equal, since they refer to a null dataset statement reference.
		bool operator!=( const const_iterator& op2) const
		{
			return !(this->operator==( op2));
		}

		///	\brief
		///		It gets the current record.
		/// \return
		///		the current record.
		/// \note
		///		This method will crash if called for an \c end iterator.
		const value_type& operator*() const
		{
			return p_reader_->current_record_;
		}

		///	\brief
		///		It gets the current record.
		/// \return
		///		the current record.
		/// \note
		///		This method will crash if called for an \c end iterator.
		const value_type& operator->() const
		{
			return p_reader_->current_record_;
		}

	protected:
		/// \internal
		///	\brief It contains the associated statement, giving the dataset to be iterated. This pointer is \c NULL for
		///		\c end iterators.
		mutable dataset_reader* p_reader_;

		bool ended_;
	};



	/// \brief
	///		It starts the iteration of the dataset, by executing it and fetching the first record.
	///	\return
	///		An iterator pointing to the first record of the dataset, or an \c end iterator if no record was available
	///		for fetching.
	/// \note
	///		It closes the previous cursor, if any, so it can support reopening of the statement even if it was not c
	///		completely iterated.
	/// \exception odbc_error
	///		Thrown if there was an error during execution of the statement or fetching the first record of the dataset.
	const_iterator begin() const
	{
		return const_iterator( const_cast< dataset_reader&>( *this));
	}

	/// \brief
	///		It gets the iterator indicating the end of the dataset.
	/// \return
	///		the \c end iterator of the dataset.
	const_iterator end() const
	{
		return const_iterator();
	}

private:
	/// \internal
	/// \brief It fetches the data of the next record.
	/// \return true if there is a record to be fetched or false otherwise.
	bool fetch()
	{
		SQLRETURN ret_code = SQLFetch( get_statement_handle( statement_));
		if( ret_code != SQL_NO_DATA)
		{
			// At this point, either some data was successfully fetched or some
			//	error occurred. Check if any error.
			throw_if_stat_failed( ret_code, get_statement_handle( statement_));

			// Successful data fetched.
			// Put it in the current record. Use execution policy.
			int fieldIndex = 0;
			BOOST_FOREACH( column_type* p_column, columns_)
			{
				current_record_[fieldIndex++] = p_column->fetch();
			}
			return true;
		}
		else
		{
			// No more records.
			return false;
		}
	}
private:
	statement& statement_;
	column_collection columns_;
	SQLSMALLINT no_of_cols_;

	/// \internal
	/// \brief It is the current record.
	/// \details
	///		When fetching, it contains the data of the current row.
	///		\n
	///		It is accessible only through the iterators.
	record_type current_record_;
};


} // namespace odbc

#endif // ODBC_DATASET_READER_HPP
